﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Concurrent;

namespace Infrastructure.Core.Loging
{
    /// <summary>
    /// 文本日志
    /// </summary>
    internal class TextLog : ILogger
    {
        //当前应用号ID
        static String appid = ConfigurationManager.AppSettings["AppId"] ?? "10001";
        //日志根目录
        static String rootPath = String.Format(@"{0}\log",AppDomain.CurrentDomain.BaseDirectory);
        
        /// <summary>
        /// 内存日志队列
        /// </summary>
        static BlockingCollection<LogDo> logCollection;
        //定时timer1去备份日志
        static System.Timers.Timer timer1;

        #region Ctors

        static TextLog() {
            logCollection = new BlockingCollection<LogDo>();
            rootPath = String.Format(@"{0}\log\{1}\", AppDomain.CurrentDomain.BaseDirectory, appid);            
            if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath);

            Task.Factory.StartNew(() =>
            {
                LogTaskProcess();
            });

            //12小时执行一次
            timer1 = new System.Timers.Timer(12 * 60 * 60 * 1000);
            timer1.Elapsed += ((o, e) =>
            {
                BackLog();
            });
            timer1.Start();
        }

        #endregion

        #region ILogger Members

        public void Info(string title, string content)
        {
            this.Write(LogLevel.Info, title, content);
        }

        public void Warn(string title, string content)
        {
            this.Write(LogLevel.Warn, title, content);
        }

        public void Error(string title, string content)
        {
            this.Write(LogLevel.Error, title, content);
        }

        public void Exception(string title, string content)
        {
            this.Write(LogLevel.Exception, title, content);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// 日志记录到内存队列
        /// </summary>
        /// <param name="logLevel"></param>
        /// <param name="title"></param>
        /// <param name="content"></param>
        private void Write(LogLevel logLevel, String title, String content)
        {
            var logDo = new LogDo(logLevel.ToString(), title, content);
            logDo.AppId = appid;
            logCollection.TryAdd(logDo);
        }

        static void Write(LogDo logDo)
        {
            String filePath = String.Empty;

            if (!CreateDirectory(logDo.LogLevel, logDo.Title, out filePath))
                return;

            //一小时生成一个文本文件
            String fileName = String.Format("{0}.txt", DateTime.Now.ToString("yyyyMMddHH"));
            String fullPath = String.Format(@"{0}\{1}", filePath, fileName);

            StringBuilder logBuilder = new StringBuilder();
            logBuilder.AppendFormat("[StartTime:{0}]\r\n", DateTime.Now.ToString("yyyyMMdd HH:mm:ss"));
            logBuilder.Append(logDo.Content);
            logBuilder.AppendFormat("\r\n[EndTime:{0}]\r\n", DateTime.Now.ToString("yyyyMMdd HH:mm:ss"));

            Write(fullPath, logBuilder.ToString());
        }

        /// <summary>
        /// 创建日志文件夹目录
        /// </summary>
        /// <param name="logLevel"></param>
        /// <param name="title"></param>
        /// <returns></returns>
        static Boolean CreateDirectory(String logLevel, String title, out String logPath)
        {
            Boolean result = false;
            logPath = String.Empty;
            try
            {
                var filePath = String.Format(@"{0}\{1}\{2}\{3}", rootPath, DateTime.Now.ToString("yyyyMMdd"), logLevel, title);
                if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath);
                logPath = filePath;
                result = true;
            }
            catch (Exception ex) {
                result = false;
            }
            return result;
        }

        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="content"></param>
        static void Write(String fileName, String content)
        {
            try
            {
                using (var fileStream = File.Open(fileName, FileMode.Append))
                {
                    byte[] data = System.Text.Encoding.Default.GetBytes(content);
                    fileStream.Write(data, 0, data.Length);
                }
            }
            catch (Exception ex)
            {
                
            }
        }

        /// <summary>
        /// 单线程下入文本日志
        /// </summary>
        static void LogTaskProcess(){
            foreach (var log in logCollection.GetConsumingEnumerable())
            {
                try
                {
                    Write(log);
                }
                catch (Exception ex)
                {

                }
            }
        }

        #endregion

        #region BackUpLog

        /// <summary>
        /// 备份日志
        /// </summary>
        static void BackLog()
        {
            try
            {
                if (!Directory.Exists(rootPath)) return;

                var today = DateTime.Now.ToString("yyyyMMdd");
                var dirs = Directory.GetDirectories(rootPath);
                if (dirs == null) return;

                foreach (var dirPath in dirs)
                {
                    if (!Directory.Exists(dirPath)) continue;
                    var directory = new DirectoryInfo(dirPath);
                    if (directory.Name == today) continue;
                    try
                    {
                        //压缩文件夹日志
                        var destFile = String.Format("{0}/{1}.gz", rootPath, directory.Name);

                        if (File.Exists(destFile)) File.Delete(destFile);
                        CompressDirectory(dirPath, destFile, 9, 4096);
                        //压缩完以后删除源文件夹
                        Directory.Delete(dirPath, true);
                    }
                    catch (Exception ex)
                    {
                        
                    }
                }
            }
            catch (Exception ex)
            {
                
            }
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="dirPath">压缩文件夹的路径</param>
        /// <param name="fileName">生成的zip文件路径</param>
        /// <param name="level">压缩级别 0 - 9 0是存储级别 9是最大压缩</param>
        /// <param name="bufferSize">读取文件的缓冲区大小</param>
        private static void CompressDirectory(string dirPath, string fileName, int level, int bufferSize)
        {
            byte[] buffer = new byte[bufferSize];
            using (ZipOutputStream s = new ZipOutputStream(File.Create(fileName)))
            {
                s.SetLevel(level);
                CompressDirectory(dirPath, dirPath, s, buffer);
                s.Finish();
                s.Close();
            }
        }

        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="root">压缩文件夹路径</param>
        /// <param name="path">压缩文件夹内当前要压缩的文件夹路径</param>
        /// <param name="s"></param>
        /// <param name="buffer">读取文件的缓冲区大小</param>
        private static void CompressDirectory(string root, string path, ZipOutputStream s, byte[] buffer)
        {
            root = root.TrimEnd('\\') + "\\";
            string[] fileNames = Directory.GetFiles(path);
            string[] dirNames = Directory.GetDirectories(path);
            string relativePath = path.Replace(root, "");
            if (relativePath != "")
            {
                relativePath = relativePath.Replace("\\", "/") + "/";
            }
            int sourceBytes;
            foreach (string file in fileNames)
            {

                ZipEntry entry = new ZipEntry(relativePath + Path.GetFileName(file));
                entry.DateTime = DateTime.Now;
                s.PutNextEntry(entry);
                using (FileStream fs = File.OpenRead(file))
                {
                    do
                    {
                        sourceBytes = fs.Read(buffer, 0, buffer.Length);
                        s.Write(buffer, 0, sourceBytes);
                    } while (sourceBytes > 0);
                }
            }

            foreach (string dirName in dirNames)
            {
                string relativeDirPath = dirName.Replace(root, "");
                ZipEntry entry = new ZipEntry(relativeDirPath.Replace("\\", "/") + "/");
                s.PutNextEntry(entry);
                CompressDirectory(root, dirName, s, buffer);
            }
        }

        /// <summary>
        /// 解压缩zip文件
        /// </summary>
        /// <param name="zipFilePath">解压的zip文件路径</param>
        /// <param name="extractPath">解压到的文件夹路径</param>
        /// <param name="bufferSize">读取文件的缓冲区大小</param>
        private static void Extract(string zipFilePath, string extractPath, int bufferSize)
        {
            extractPath = extractPath.TrimEnd('\\') + "\\";
            byte[] data = new byte[bufferSize];
            int size;
            using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipFilePath)))
            {
                ZipEntry entry;
                while ((entry = s.GetNextEntry()) != null)
                {
                    string directoryName = Path.GetDirectoryName(entry.Name);
                    string fileName = Path.GetFileName(entry.Name);

                    //先创建目录
                    if (directoryName.Length > 0)
                    {
                        Directory.CreateDirectory(extractPath + directoryName);
                    }

                    if (fileName != String.Empty)
                    {
                        using (FileStream streamWriter = File.Create(extractPath + entry.Name.Replace("/", "\\")))
                        {
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        #endregion

    }
}
